package com.crazy.multids.config.common;

import com.alibaba.druid.pool.DruidDataSource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 此类的描述是：
 *
 * @author crazyhu@wezhuiyi.com
 * @date 2018-09-16 12:38
 **/
@Slf4j
@Configuration
public class MultipleDataSourceAutoConfiguration implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware {

    private ApplicationContext applicationContext;

    private final String DS_TYPE_KEY = "crazy.datasource.type";
    private Class<? extends DataSource> dataSourceClass;

    /***
     * 第一个必须是master数据源
     */
    @Bean
    public MultipleDataSource multiDatasource() {
        List<String> dataSources = new ArrayList<String>();
        dataSources.add(DataSourceNames.DS_MASTER);
        dataSources.add(DataSourceNames.DS_SLAVE_0);
        dataSources.add(DataSourceNames.DS_SLAVE_1);
        return new MultipleDataSource(dataSources);
    }

    @Bean
    public DataSourcePropertiesProcessor dataSourcePropertiesProcessor() {
        return new DataSourcePropertiesProcessor();
    }

    @Bean
    public DataSourceInterceptor dataSourceInterceptor() {
        return new DataSourceInterceptor();
    }

    /**
     * 根据配置的MultipleDataSource, 注册多个数据源bean
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        MultipleDataSource multipleDataSource = applicationContext.getBean(MultipleDataSource.class);

        if (multipleDataSource == null) {
            log.error("配置了MultipleDataSourceConfiguration就一定要定义MultipleDataSource");
        }

        List<String> dataSources = multipleDataSource.getDataSources();
        String defaultDataSource = multipleDataSource.getDefaultDataSource();

        String dataSourceTypeName = applicationContext.getEnvironment().getProperty(DS_TYPE_KEY);
        if (dataSourceTypeName == null) {
            Assert.notNull(dataSourceTypeName, DS_TYPE_KEY + "没有设置");
        }
        try {
            dataSourceClass = (Class<? extends DataSource>) ClassUtils.forName(dataSourceTypeName, this.getClass().getClassLoader());
        } catch (ClassNotFoundException e) {
            log.error("类加载异常", e);
        }
        for (String ds : dataSources) {
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(dataSourceClass);
            if (ds.equalsIgnoreCase(defaultDataSource)) {
                builder.getBeanDefinition().setPrimary(true);
            }
            registry.registerBeanDefinition(ds, builder.getBeanDefinition());
        }
    }

    /**
     * 根据配置文件处理数据源配置
     */
    @Bean(name = MultipleDataSourceInitializer.BEAN_NAME)
    public MultipleDataSourceInitializer multipleDataSourceInitializer(MultipleDataSource multipleDataSource,
                                                                       DataSourcePropertiesProcessor processor) {
        for (String ds : multipleDataSource.getDataSources()) {
            DruidDataSource dataSource = applicationContext.getBean(ds, DruidDataSource.class);
            //读取配置文件
            processor.postProcessBeforeInitialization(dataSource, ds, ds);
        }

        return new MultipleDataSourceInitializer();
    }

    @Bean(name = "routingDataSourceProxy")
    @ConditionalOnMissingBean
    public AbstractRoutingDataSource routingDataSourceProxy() {

        RoutingDataSourceProxy routingDataSourceProxy = new RoutingDataSourceProxy();

        String[] beanNames = applicationContext.getBeanNamesForType(dataSourceClass);
        Map<Object, Object> targetDataSources = new HashMap<>();
        for (String beanName : beanNames) {
            DataSource dataSource = applicationContext.getBean(beanName, dataSourceClass);
            // 设置默认数据源
            if (beanName.equalsIgnoreCase(DataSourceNames.DS_MASTER)) {
                routingDataSourceProxy.setDefaultTargetDataSource(dataSource);
            }
            targetDataSources.put(beanName, dataSource);
        }
        routingDataSourceProxy.setTargetDataSources(targetDataSources);
        return routingDataSourceProxy;
    }

    @Bean(name = "transactionManager")
    @ConditionalOnMissingBean
    public PlatformTransactionManager transactionManager(AbstractRoutingDataSource dynamicDataSource) {
        return new DataSourceTransactionManager(dynamicDataSource);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
